package com.garry.core;

import com.garry.core.context.CloudContext;
import com.garry.core.exception.CloudNodeIntialException;
import com.garry.core.node.*;
import com.garry.core.type.IdentityEnum;
import org.apache.commons.lang.StringUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.InputStream;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.HashSet;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Created by GarryGe on 2016/12/28.
 */
public class CloudSystem {
    private static String IDENTITY = null;

    public static CloudNode node;

    private static String localIp;

    public static void initialize() throws Exception {
        localIp = getLocalIp();
        config();
    }

    private static String getLocalIp() throws CloudNodeIntialException {
        String ip = null;
        try {
            ip = InetAddress.getLocalHost().getHostAddress();
        } catch (UnknownHostException e) {
            e.printStackTrace();
            throw new CloudNodeIntialException("failed to get local ip address");
        }
        return ip;
    }

    private static void config() throws Exception {
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = dbf.newDocumentBuilder();
        InputStream in = CloudSystem.class.getClassLoader().getResourceAsStream("configuration.xml");
        Document doc = builder.parse(in);
        // root <university>
        Element root = doc.getDocumentElement();
        if (root == null) return;
        // all college node
        NodeList configurations = root.getChildNodes();
        if (configurations == null) return;
        NodeList nodes = root.getElementsByTagName("nodes").item(0).getChildNodes();
        Set<String> ips = new HashSet<>();
        for (int i = 0; i < nodes.getLength(); i++) {
            if (nodes.item(i).getNodeType() == Node.ELEMENT_NODE) {
                Element node = (Element) nodes.item(i);
                String type = node.getElementsByTagName("type").item(0).getFirstChild().getNodeValue();
                String name = node.getElementsByTagName("name").item(0).getFirstChild().getNodeValue();
                String ip = node.getElementsByTagName("ip").item(0).getFirstChild().getNodeValue();
                if (!ipcheck(ip)) {
                    throw new Exception("node" + i + "'s ip:" + ip + " is incorrect.");
                }
                if (ips.contains(ip)) {
                    //throw new Exception("ip:" + ip + " is repeated");
                } else {
                    ips.add(ip);
                }
                switch (type) {
                    case "namenode":
                        if (StringUtils.equals(ip, localIp)) {
                            CloudSystem.node = new NameNode(name, ip);
                        } else {
                            CloudContext.nameNode = new RemoteNameNode(name, ip);
                        }
                        break;
                    case "datanode":
                        if (StringUtils.equals(ip, localIp)) {
                            String directoryPath = node.getElementsByTagName("directory").item(0).getFirstChild().getNodeValue();
                            CloudSystem.node = new DataNode(name, ip);
                        } else {
                            CloudContext.dataNodeMap.put(name, new RemoteDataNode(name, ip));
                        }
                        break;
                    case "backupnode":
                        if (StringUtils.equals(ip, localIp)) {
                            CloudSystem.node = new BackUpNode(name, ip);
                        }
                        break;
                    default:
                        break;
                }
            }
        }
    }

    private static boolean ipcheck(String nodeIp) {
        final String regex = "([1-9]|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])(\\.(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])){3}";
        Pattern pat = Pattern.compile(regex);
        Matcher mat = pat.matcher(nodeIp);
        boolean ipAddress = mat.find();
        return ipAddress;
    }
}

